from functools import wraps, partial
import logging


def attach_wrapper(obj, func=None):
    if func is None:
        return partial(attach_wrapper, obj)  # 相当于attch_wrapper(obj),但是调用的时候需要加()
    setattr(obj, func.__name__, func)  # obj.xxx = func
    return func


def logged(level, name=None, msg=None):  # 装饰器生成
    def decorate(func):
        logname = name if name else func.__module__
        log = logging.getLogger(logname)
        logmsg = msg if msg else func.__name__

        @wraps(func)
        def wrapper(*args, **kw):
            log.log(level, logmsg) #log.log(logging.Debug，logmsg)
            return func(*args, **kw)

        @attach_wrapper(wrapper)  # warpper.set_level(newlevel)
        def set_level(newlevel):
            nonlocal level
            level = newlevel

        @attach_wrapper(wrapper)
        def set_message(newmsg):
            nonlocal logmsg
            logmsg = newmsg

        return wrapper

    return decorate


@logged(logging.DEBUG)
def add(x, y):
    return x + y


@logged(logging.CRITICAL, 'example')
def spam():
    print('Spam!')


if __name__ == '__main__':
    import logging

    #logging.basicConfig(level=logging.DEBUG)
    add(2, 3)
    '''
    DEBUG: __main__:add
    5
    '''

    # Change the log message
    add.set_message('Add called')
    add(2, 3)
    '''
    DEBUG: __main__:Add called
    5
    '''

    # Change the log level
    add.set_level(logging.WARNING)
    add(2, 3)
    '''
    WARNING: __main__:Add called
    5
    '''
